Repository navigation
Conversation
This PR adds the ability to discover the images necessary for running in an airgap environment.
dminnear-rh
left a comment
There was a problem hiding this comment.
LGTM overall but I'd like the Makefile-common logic moved to rhvp.cluster_utils if you're happy with that
| } | ||
|
|
||
| // WriteChecklist writes CHECKLIST.md. | ||
| func WriteChecklist(inv *PatternInventory, opts *Options, outputDir string) error { |
There was a problem hiding this comment.
Why are we using string.Builder here for making this file rather than a template like we do in WriteMirrorCommands above?
| func dashVersion(ocpVersion string) string { | ||
| out := make([]rune, 0, len(ocpVersion)) | ||
| for _, r := range ocpVersion { | ||
| if r == '.' { | ||
| out = append(out, '-') | ||
| } else { | ||
| out = append(out, r) | ||
| } | ||
| } | ||
| return string(out) | ||
| } |
There was a problem hiding this comment.
Why use this helper rather than
dashVersion := strings.Replaceall(ocpVersion, '.', '-')|
|
||
| ##@ Disconnected / Airgap Tasks | ||
| # Optional overrides for airgap-init (passed through when set). | ||
| # AIRGAP_OCP_VERSION adds mirror.platform channel stable-<version> when set. | ||
| # AIRGAP_REGISTRY is optional for values-disconnected / mirror-commands helpers. | ||
| AIRGAP_OCP_VERSION ?= | ||
| AIRGAP_REGISTRY ?= | ||
| AIRGAP_OUTPUT_DIR ?= ./airgap-out | ||
| AIRGAP_PATTERNIZER_IMAGE ?= quay.io/validatedpatterns/patternizer | ||
| AIRGAP_DOWNLOAD_OC_MIRROR ?= false | ||
| AIRGAP_EXTRA_OPTS ?= | ||
| # install-airgap / airgap-bootstrap (values-secret airgap: section) | ||
| AIRGAP_BOOTSTRAP_DIR ?= ./airgap-bootstrap | ||
| AIRGAP_SECRETS_FILE ?= | ||
| AIRGAP_PATTERNIZER_BIN ?= | ||
| AIRGAP_WAIT_SECONDS ?= 1200 | ||
| AIRGAP_WAIT_INTERVAL ?= 15 | ||
| AIRGAP_ARGOCD_NS ?= vp-gitops | ||
| AIRGAP_REPO_SERVER ?= $(AIRGAP_ARGOCD_NS)-repo-server | ||
|
|
||
| .PHONY: airgap-init | ||
| airgap-init: ## Generate oc-mirror imagesetconfig.yaml and disconnected init artifacts | ||
| @$(ANSIBLE_RUN) rhvp.cluster_utils.airgap_init \ | ||
| -e airgap_ocp_version="$(AIRGAP_OCP_VERSION)" \ | ||
| -e airgap_registry="$(AIRGAP_REGISTRY)" \ | ||
| -e airgap_output_dir=$(AIRGAP_OUTPUT_DIR) \ | ||
| -e airgap_patternizer_image=$(AIRGAP_PATTERNIZER_IMAGE) \ | ||
| -e airgap_download_oc_mirror=$(AIRGAP_DOWNLOAD_OC_MIRROR) \ | ||
| -e airgap_extra_opts="$(AIRGAP_EXTRA_OPTS)" | ||
|
|
||
| # Resolve patternizer: explicit bin, PATH, ~/patternizer, or container image. | ||
| # Usage in a recipe: $(call AIRGAP_PATTERNIZER_EXEC,airgap bootstrap "$(CURDIR)" --output-dir "$(AIRGAP_BOOTSTRAP_DIR)") | ||
| define AIRGAP_PATTERNIZER_EXEC | ||
| set -euo pipefail; \ | ||
| BIN="$(AIRGAP_PATTERNIZER_BIN)"; \ | ||
| if [ -z "$$BIN" ] && command -v patternizer >/dev/null 2>&1; then BIN="$$(command -v patternizer)"; fi; \ | ||
| if [ -z "$$BIN" ] && [ -x "$(HOME)/patternizer" ]; then BIN="$(HOME)/patternizer"; fi; \ | ||
| if [ -z "$$BIN" ] && [ -x "$(HOME)/bin/patternizer" ]; then BIN="$(HOME)/bin/patternizer"; fi; \ | ||
| if [ -n "$$BIN" ]; then \ | ||
| echo "==> Using patternizer at $$BIN"; \ | ||
| "$$BIN" $(1); \ | ||
| elif command -v podman >/dev/null 2>&1; then \ | ||
| echo "==> Using podman $(AIRGAP_PATTERNIZER_IMAGE)"; \ | ||
| podman run --rm --network=host \ | ||
| -v "$(CURDIR):$(CURDIR):z" -v "$(HOME):$(HOME):z" -w "$(CURDIR)" \ | ||
| -e HOME="$(HOME)" \ | ||
| $(AIRGAP_PATTERNIZER_IMAGE) $(1); \ | ||
| elif command -v docker >/dev/null 2>&1; then \ | ||
| echo "==> Using docker $(AIRGAP_PATTERNIZER_IMAGE)"; \ | ||
| docker run --rm --network=host \ | ||
| -v "$(CURDIR):$(CURDIR):z" -v "$(HOME):$(HOME):z" -w "$(CURDIR)" \ | ||
| -e HOME="$(HOME)" \ | ||
| $(AIRGAP_PATTERNIZER_IMAGE) $(1); \ | ||
| else \ | ||
| echo "patternizer not found. Install it, set AIRGAP_PATTERNIZER_BIN, or install podman/docker." >&2; \ | ||
| exit 1; \ | ||
| fi | ||
| endef | ||
|
|
||
| .PHONY: airgap-bootstrap | ||
| airgap-bootstrap: ## Generate airgap trust artifacts from values-secret airgap: section | ||
| @SECRETS_OPT=""; \ | ||
| if [ -n "$(AIRGAP_SECRETS_FILE)" ]; then SECRETS_OPT="--secrets-file $(AIRGAP_SECRETS_FILE)"; fi; \ | ||
| $(call AIRGAP_PATTERNIZER_EXEC,airgap bootstrap "$(CURDIR)" --output-dir "$(AIRGAP_BOOTSTRAP_DIR)" $$SECRETS_OPT) | ||
|
|
||
| .PHONY: airgap-trust-pre | ||
| airgap-trust-pre: airgap-bootstrap ## Apply cluster Proxy CA trust (before pattern install) | ||
| @test -x "$(AIRGAP_BOOTSTRAP_DIR)/apply-airgap.sh" || { echo "missing $(AIRGAP_BOOTSTRAP_DIR)/apply-airgap.sh; run make airgap-bootstrap" >&2; exit 1; } | ||
| @AIRGAP_WAIT_SECONDS="$(AIRGAP_WAIT_SECONDS)" AIRGAP_WAIT_INTERVAL="$(AIRGAP_WAIT_INTERVAL)" \ | ||
| "$(AIRGAP_BOOTSTRAP_DIR)/apply-airgap.sh" pre | ||
|
|
||
| .PHONY: airgap-wait-gitops | ||
| airgap-wait-gitops: ## Wait for GitOps namespace / Argo CD (runs while install is in progress) | ||
| @set -euo pipefail; \ | ||
| NS="$(AIRGAP_ARGOCD_NS)"; \ | ||
| REPO="$(AIRGAP_REPO_SERVER)"; \ | ||
| WAIT="$(AIRGAP_WAIT_SECONDS)"; \ | ||
| INTERVAL="$(AIRGAP_WAIT_INTERVAL)"; \ | ||
| if ! command -v oc >/dev/null 2>&1; then echo "oc is required" >&2; exit 1; fi; \ | ||
| echo "==> Waiting up to $${WAIT}s for namespace/$${NS}"; \ | ||
| elapsed=0; \ | ||
| while [ "$$elapsed" -lt "$$WAIT" ]; do \ | ||
| if oc get namespace "$$NS" >/dev/null 2>&1; then echo " namespace/$${NS} is present"; break; fi; \ | ||
| sleep "$$INTERVAL"; \ | ||
| elapsed=$$((elapsed + INTERVAL)); \ | ||
| if [ "$$elapsed" -ge "$$WAIT" ]; then echo "timed out waiting for namespace/$${NS}" >&2; exit 1; fi; \ | ||
| done; \ | ||
| echo "==> Waiting up to $${WAIT}s for configmap/argocd-tls-certs-cm in $${NS} (optional if only repo-creds)"; \ | ||
| elapsed=0; \ | ||
| while [ "$$elapsed" -lt "$$WAIT" ]; do \ | ||
| if oc get configmap argocd-tls-certs-cm -n "$$NS" >/dev/null 2>&1; then \ | ||
| echo " configmap/argocd-tls-certs-cm is present"; \ | ||
| break; \ | ||
| fi; \ | ||
| sleep "$$INTERVAL"; \ | ||
| elapsed=$$((elapsed + INTERVAL)); \ | ||
| if [ "$$elapsed" -ge "$$WAIT" ]; then \ | ||
| echo " warning: argocd-tls-certs-cm not ready yet; airgap-trust-post will keep waiting" >&2; \ | ||
| break; \ | ||
| fi; \ | ||
| done; \ | ||
| echo "==> Waiting up to $${WAIT}s for deployment/$${REPO} in $${NS} (optional)"; \ | ||
| elapsed=0; \ | ||
| while [ "$$elapsed" -lt "$$WAIT" ]; do \ | ||
| if oc get deployment "$$REPO" -n "$$NS" >/dev/null 2>&1; then \ | ||
| echo " deployment/$${REPO} is present"; \ | ||
| break; \ | ||
| fi; \ | ||
| sleep "$$INTERVAL"; \ | ||
| elapsed=$$((elapsed + INTERVAL)); \ | ||
| if [ "$$elapsed" -ge "$$WAIT" ]; then \ | ||
| echo " warning: deployment/$${REPO} not ready yet; post will keep waiting" >&2; \ | ||
| break; \ | ||
| fi; \ | ||
| done; \ | ||
| echo "==> GitOps wait complete; running airgap-trust-post next" | ||
|
|
||
| .PHONY: airgap-trust-post | ||
| airgap-trust-post: ## Apply Argo CD TLS + repo-creds (waits for vp-gitops / Argo CD resources) | ||
| @test -x "$(AIRGAP_BOOTSTRAP_DIR)/apply-airgap.sh" || { echo "missing $(AIRGAP_BOOTSTRAP_DIR)/apply-airgap.sh; run make airgap-bootstrap first" >&2; exit 1; } | ||
| @AIRGAP_WAIT_SECONDS="$(AIRGAP_WAIT_SECONDS)" AIRGAP_WAIT_INTERVAL="$(AIRGAP_WAIT_INTERVAL)" \ | ||
| "$(AIRGAP_BOOTSTRAP_DIR)/apply-airgap.sh" post | ||
|
|
||
| .PHONY: airgap-restore-proxy | ||
| airgap-restore-proxy: ## Restore proxy/cluster from airgap bootstrap backup | ||
| @test -x "$(AIRGAP_BOOTSTRAP_DIR)/restore-proxy-cluster.sh" || { echo "missing $(AIRGAP_BOOTSTRAP_DIR)/restore-proxy-cluster.sh" >&2; exit 1; } | ||
| @"$(AIRGAP_BOOTSTRAP_DIR)/restore-proxy-cluster.sh" | ||
|
|
||
| # Stop a background make install (and its descendants). Used by Ctrl-C cleanup and airgap-abort. | ||
| # Args: $1 = root pid. Sends TERM to the process group / tree, waits briefly, then KILL. | ||
| define AIRGAP_STOP_INSTALL | ||
| pid="$(1)"; \ | ||
| if [ -n "$$pid" ] && kill -0 "$$pid" 2>/dev/null; then \ | ||
| echo "==> Stopping background make install (pid $$pid)"; \ | ||
| kill -TERM -"$$pid" 2>/dev/null || kill -TERM "$$pid" 2>/dev/null || true; \ | ||
| if command -v pkill >/dev/null 2>&1; then pkill -TERM -P "$$pid" 2>/dev/null || true; fi; \ | ||
| i=0; \ | ||
| while [ "$$i" -lt 8 ] && kill -0 "$$pid" 2>/dev/null; do sleep 1; i=$$((i + 1)); done; \ | ||
| if kill -0 "$$pid" 2>/dev/null; then \ | ||
| echo "==> Force-killing background make install (pid $$pid)"; \ | ||
| kill -KILL -"$$pid" 2>/dev/null || kill -KILL "$$pid" 2>/dev/null || true; \ | ||
| if command -v pkill >/dev/null 2>&1; then pkill -KILL -P "$$pid" 2>/dev/null || true; fi; \ | ||
| fi; \ | ||
| wait "$$pid" 2>/dev/null || true; \ | ||
| fi | ||
| endef | ||
|
|
||
| .PHONY: airgap-abort | ||
| airgap-abort: ## Stop a background install-airgap session (reads airgap-bootstrap/install.pid) | ||
| @set -euo pipefail; \ | ||
| PIDFILE="$(AIRGAP_BOOTSTRAP_DIR)/install.pid"; \ | ||
| if [ ! -f "$$PIDFILE" ]; then \ | ||
| echo "No $$PIDFILE - nothing to abort (is install-airgap running?)" >&2; \ | ||
| exit 1; \ | ||
| fi; \ | ||
| INSTALL_PID=$$(tr -d '[:space:]' < "$$PIDFILE"); \ | ||
| $(call AIRGAP_STOP_INSTALL,$$INSTALL_PID); \ | ||
| rm -f "$$PIDFILE"; \ | ||
| echo "==> Aborted. Log: $(AIRGAP_BOOTSTRAP_DIR)/install.log" | ||
|
|
||
| .PHONY: install-airgap | ||
| install-airgap: ## Disconnected install: Proxy CA, background install, wait for GitOps, post TLS/creds, then await install | ||
| @$(MAKE) airgap-trust-pre | ||
| @set -euo pipefail; \ | ||
| mkdir -p "$(AIRGAP_BOOTSTRAP_DIR)"; \ | ||
| LOG="$(AIRGAP_BOOTSTRAP_DIR)/install.log"; \ | ||
| PIDFILE="$(AIRGAP_BOOTSTRAP_DIR)/install.pid"; \ | ||
| echo "==> Starting make install in background (log: $$LOG)"; \ | ||
| echo " (post trust runs as soon as vp-gitops is ready — install need not finish first)"; \ | ||
| echo " Ctrl-C or: make airgap-abort"; \ | ||
| setsid $(MAKE) install >"$$LOG" 2>&1 < /dev/null & \ | ||
| INSTALL_PID=$$!; \ | ||
| echo "$$INSTALL_PID" > "$$PIDFILE"; \ | ||
| cleanup() { \ | ||
| trap - INT TERM; \ | ||
| $(call AIRGAP_STOP_INSTALL,$$INSTALL_PID); \ | ||
| rm -f "$$PIDFILE"; \ | ||
| }; \ | ||
| trap cleanup INT TERM; \ | ||
| if ! $(MAKE) airgap-wait-gitops; then \ | ||
| cleanup; \ | ||
| echo "airgap-wait-gitops failed; see $$LOG" >&2; \ | ||
| exit 1; \ | ||
| fi; \ | ||
| if ! $(MAKE) airgap-trust-post; then \ | ||
| trap - INT TERM; \ | ||
| echo "airgap-trust-post failed; leaving install running (pid $$INSTALL_PID). See $$LOG" >&2; \ | ||
| echo " Re-run: make airgap-trust-post" >&2; \ | ||
| echo " Or abort: make airgap-abort" >&2; \ | ||
| exit 1; \ | ||
| fi; \ | ||
| echo "==> Argo CD trust applied; waiting for background make install (pid $$INSTALL_PID)..."; \ | ||
| set +e; \ | ||
| wait $$INSTALL_PID; \ | ||
| INSTALL_RC=$$?; \ | ||
| set -e; \ | ||
| trap - INT TERM; \ | ||
| rm -f "$$PIDFILE"; \ | ||
| if [ "$$INSTALL_RC" -ne 0 ]; then \ | ||
| echo "make install failed with exit $$INSTALL_RC; see $$LOG" >&2; \ | ||
| tail -n 40 "$$LOG" >&2 || true; \ | ||
| exit $$INSTALL_RC; \ | ||
| fi; \ | ||
| echo "install-airgap complete. Proxy backup: $(AIRGAP_BOOTSTRAP_DIR)/backup/proxy-cluster.yaml"; \ | ||
| echo " install log: $$LOG" |
There was a problem hiding this comment.
I'd prefer that the complexity of these Makefile-common targets be part of the rhvp.cluster_utils collection rather than in this makefile.
Makes it so that updating the airgap recipes across the board does not require updating each pattern separately and it's consistent with all the other targets.
You will probably also need to add those AIRGAP_* env vars as arguments to the invocation of the utility container at the end of pattern.sh as well since right now if someone had AIRGAP_REGISTRY=some-registry ./pattern.sh make airgap-init I don't believe that env var would be respected
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Airgap / disconnected GitOps trust (patternizer airgap bootstrap ONLY). | ||
| # Not loaded into Vault by make load-secrets. Copy this file to | ||
| # ~/values-secret-<pattern>.yaml (or keep paths local) and fill in real paths. | ||
| # | ||
| # UX: | ||
| # Copy this template to ~/values-secret-<pattern>.yaml and fill in airgap: paths. | ||
| # ./pattern.sh make install-airgap | ||
| # | ||
| # Or manually: | ||
| # patternizer airgap bootstrap /path/to/pattern --output-dir ./airgap-bootstrap | ||
| # ./airgap-bootstrap/apply-airgap.sh pre # before install | ||
| # ./pattern.sh make install | ||
| # ./airgap-bootstrap/apply-airgap.sh post # after vp-gitops exists | ||
| # | ||
| # clusterProxy: CA for patterns-operator git clone (Proxy/user-ca-bundle) | ||
| # argocdTLS: CA for Argo CD (argocd-tls-certs-cm + trusted-ca-bundle) | ||
| # registries: OCI/Helm mirror credentials for Argo CD repo-creds | ||
| # --------------------------------------------------------------------------- | ||
| # airgap: | ||
| # argocdNamespace: vp-gitops | ||
| # patternsOperatorNamespace: patterns-operator | ||
| # certificates: | ||
| # - name: gitea | ||
| # fields: | ||
| # - name: host | ||
| # value: gitea.airgap.rhuk.lab | ||
| # - name: ca | ||
| # path: /home/fedora/gitea_container_certs/cert.pem | ||
| # - name: clusterProxy | ||
| # value: "true" | ||
| # - name: argocdTLS | ||
| # value: "true" | ||
| # - name: mirror-registry | ||
| # fields: | ||
| # - name: host | ||
| # value: mirror.airgap.rhuk.lab | ||
| # - name: ca | ||
| # path: /etc/pki/ca-trust/source/anchors/quay-rootCA.pem | ||
| # - name: clusterProxy | ||
| # value: "true" | ||
| # - name: argocdTLS | ||
| # value: "true" | ||
| # registries: | ||
| # - name: mirror | ||
| # fields: | ||
| # - name: url | ||
| # value: mirror.airgap.rhuk.lab:8443 | ||
| # - name: dockerconfig | ||
| # path: ~/.docker/config.json | ||
| # # Or username + password file instead of dockerconfig: | ||
| # # - name: username | ||
| # # value: init | ||
| # # - name: password | ||
| # # path: /path/to/registry.password | ||
| # | ||
| # # Disconnected pattern values (rendered into values-global.yaml + variants) | ||
| # registry: mirror.airgap.rhuk.lab:8443 | ||
| # ocpVersion: "4.22" | ||
| # # Or declare CatalogSources explicitly (overrides ocpVersion derivation): | ||
| # # catalogSources: | ||
| # # redhat: cs-redhat-operator-index-v4-22 | ||
| # # community: cs-community-operator-index-v4-22 | ||
| # # Or discover from the cluster (requires oc / kubeconfig): | ||
| # # discoverCatalogs: true | ||
| # # renderValues: true # default when registry/ocpVersion/catalogSources set |
There was a problem hiding this comment.
this will be added to every single repo created with patternizer with secrets. is that intended?
| @@ -1,6 +1,6 @@ | |||
| # Patternizer | |||
|
|
|||
|  | |||
There was a problem hiding this comment.
that's quite a leap in minor version
This PR adds the ability to discover the images necessary for running
in an airgap environment.
It will scrape the pattern repository and generate the imagesetconfig.yaml
for mirroring the proper images.